home *** CD-ROM | disk | FTP | other *** search
/ The Atari Compendium / The Atari Compendium (Toad Computers) (1994).iso / files / prgtools / mint / utilit~1 / initsnb.zoo / init / sh / exec.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-09-09  |  18.0 KB  |  854 lines

  1. /*-
  2.  * Copyright (c) 1991 The Regents of the University of California.
  3.  * All rights reserved.
  4.  *
  5.  * This code is derived from software contributed to Berkeley by
  6.  * Kenneth Almquist.
  7.  *
  8.  * Redistribution and use in source and binary forms, with or without
  9.  * modification, are permitted provided that the following conditions
  10.  * are met:
  11.  * 1. Redistributions of source code must retain the above copyright
  12.  *    notice, this list of conditions and the following disclaimer.
  13.  * 2. Redistributions in binary form must reproduce the above copyright
  14.  *    notice, this list of conditions and the following disclaimer in the
  15.  *    documentation and/or other materials provided with the distribution.
  16.  * 3. All advertising materials mentioning features or use of this software
  17.  *    must display the following acknowledgement:
  18.  *    This product includes software developed by the University of
  19.  *    California, Berkeley and its contributors.
  20.  * 4. Neither the name of the University nor the names of its contributors
  21.  *    may be used to endorse or promote products derived from this software
  22.  *    without specific prior written permission.
  23.  *
  24.  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  25.  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  26.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  27.  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  28.  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  29.  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  30.  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  31.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  32.  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  33.  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  34.  * SUCH DAMAGE.
  35.  */
  36.  
  37. #ifndef lint
  38. static char sccsid[] = "@(#)exec.c    5.2 (Berkeley) 3/13/91";
  39. #endif /* not lint */
  40.  
  41. /*
  42.  * When commands are first encountered, they are entered in a hash table.
  43.  * This ensures that a full path search will not have to be done for them
  44.  * on each invocation.
  45.  *
  46.  * We should investigate converting to a linear search, even though that
  47.  * would make the command name "hash" a misnomer.
  48.  */
  49.  
  50. #include "shell.h"
  51. #include "main.h"
  52. #include "nodes.h"
  53. #include "parser.h"
  54. #include "redir.h"
  55. #include "eval.h"
  56. #include "exec.h"
  57. #include "builtins.h"
  58. #include "var.h"
  59. #include "options.h"
  60. #include "input.h"
  61. #include "output.h"
  62. #include "syntax.h"
  63. #include "memalloc.h"
  64. #include "error.h"
  65. #include "init.h"
  66. #include "mystring.h"
  67. #include <sys/types.h>
  68. #include <sys/stat.h>
  69. #include <fcntl.h>
  70. #include <errno.h>
  71.  
  72. #ifdef __MINT__
  73. /* Defines order in which to look for executable program suffixes */
  74. static const char *const suffixes[] = {
  75.     "", ".ttp", ".tos", ".prg", 0
  76. };
  77. #endif
  78.  
  79. #define CMDTABLESIZE 31        /* should be prime */
  80. #define ARB 1            /* actual size determined at run time */
  81.  
  82.  
  83.  
  84. struct tblentry {
  85.     struct tblentry *next;    /* next entry in hash chain */
  86.     union param param;    /* definition of builtin function */
  87.     short cmdtype;        /* index identifying command */
  88.     char rehash;        /* if set, cd done since entry created */
  89.     char cmdname[ARB];    /* name of command */
  90. };
  91.  
  92.  
  93. STATIC struct tblentry *cmdtable[CMDTABLESIZE];
  94. STATIC int builtinloc = -1;        /* index in path of %builtin, or -1 */
  95.  
  96.  
  97. #ifdef __STDC__
  98. STATIC void tryexec(char *, char **, char **);
  99. STATIC void execinterp(char **, char **);
  100. STATIC void printentry(struct tblentry *);
  101. STATIC void clearcmdentry(int);
  102. STATIC struct tblentry *cmdlookup(char *, int);
  103. STATIC void delete_cmd_entry(void);
  104. #else
  105. STATIC void tryexec();
  106. STATIC void execinterp();
  107. STATIC void printentry();
  108. STATIC void clearcmdentry();
  109. STATIC struct tblentry *cmdlookup();
  110. STATIC void delete_cmd_entry();
  111. #endif
  112.  
  113.  
  114.  
  115. /*
  116.  * Exec a program.  Never returns.  If you change this routine, you may
  117.  * have to change the find_command routine as well.
  118.  */
  119.  
  120. void
  121. shellexec(argv, envp, path, index)
  122.     char **argv, **envp;
  123.     char *path;
  124.     {
  125.     char *cmdname;
  126.     int e;
  127.  
  128.     if (strchr(argv[0], '/') != NULL) {
  129.         tryexec(argv[0], argv, envp);
  130.         e = errno;
  131.     } else {
  132.         e = ENOENT;
  133.         while ((cmdname = padvance(&path, argv[0])) != NULL) {
  134.             if (--index < 0 && pathopt == NULL) {
  135. #ifndef ENOTDIR
  136. #define ENOTDIR ENOENT
  137. #endif
  138.                 tryexec(cmdname, argv, envp);
  139.                 if (errno != ENOENT && errno != ENOTDIR)
  140.                     e = errno;
  141.             }
  142.             stunalloc(cmdname);
  143.         }
  144.     }
  145.     error2(argv[0], errmsg(e, E_EXEC));
  146. }
  147.  
  148.  
  149. STATIC void
  150. tryexec(cmd, argv, envp)
  151.     char *cmd;
  152.     char **argv;
  153.     char **envp;
  154.     {
  155.     int e;
  156.     char *p;
  157. #ifdef __MINT__
  158.     const char *const *suffix;
  159.     char *name_end;
  160. #endif
  161.  
  162. #ifdef SYSV
  163.     do {
  164.         TRACE(("trying to exec \"%s\"\n", cmd));
  165.         execve(cmd, argv, envp);
  166.     } while (errno == EINTR);
  167. #else
  168. #ifdef __MINT__
  169.     name_end = cmd + strlen (cmd);
  170.     for (suffix = suffixes; *suffix != 0; suffix++) {
  171.         (void) strcpy (name_end, *suffix);
  172.         TRACE(("trying to exec \"%s\"\n", cmd));
  173.         execve(cmd, argv, envp);
  174.     }
  175. #else
  176.     TRACE(("trying to exec \"%s\"\n", cmd));
  177.     execve(cmd, argv, envp);
  178. #endif
  179. #endif
  180.     e = errno;
  181.     if (e == ENOEXEC) {
  182.         initshellproc();
  183.         setinputfile(cmd, 0);
  184.         commandname = arg0 = savestr(argv[0]);
  185. #ifndef BSD
  186.         pgetc(); pungetc();        /* fill up input buffer */
  187.         p = parsenextc;
  188.         if (parsenleft > 2 && p[0] == '#' && p[1] == '!') {
  189.             argv[0] = cmd;
  190.             execinterp(argv, envp);
  191.         }
  192. #endif
  193.         setparam(argv + 1);
  194.         exraise(EXSHELLPROC);
  195.         /*NOTREACHED*/
  196.     }
  197.     errno = e;
  198. }
  199.  
  200.  
  201. #ifndef BSD
  202. /*
  203.  * Execute an interpreter introduced by "#!", for systems where this
  204.  * feature has not been built into the kernel.  If the interpreter is
  205.  * the shell, return (effectively ignoring the "#!").  If the execution
  206.  * of the interpreter fails, exit.
  207.  *
  208.  * This code peeks inside the input buffer in order to avoid actually
  209.  * reading any input.  It would benefit from a rewrite.
  210.  */
  211.  
  212. #define NEWARGS 5
  213.  
  214. STATIC void
  215. execinterp(argv, envp)
  216.     char **argv, **envp;
  217.     {
  218.     int n;
  219.     char *inp;
  220.     char *outp;
  221.     char c;
  222.     char *p;
  223.     char **ap;
  224.     char *newargs[NEWARGS];
  225.     int i;
  226.     char **ap2;
  227.     char **new;
  228.  
  229.     n = parsenleft - 2;
  230.     inp = parsenextc + 2;
  231.     ap = newargs;
  232.     for (;;) {
  233.         while (--n >= 0 && (*inp == ' ' || *inp == '\t'))
  234.             inp++;
  235.         if (n < 0)
  236.             goto bad;
  237.         if ((c = *inp++) == '\n')
  238.             break;
  239.         if (ap == &newargs[NEWARGS])
  240. bad:          error("Bad #! line");
  241.         STARTSTACKSTR(outp);
  242.         do {
  243.             STPUTC(c, outp);
  244.         } while (--n >= 0 && (c = *inp++) != ' ' && c != '\t' && c != '\n');
  245.         STPUTC('\0', outp);
  246.         n++, inp--;
  247.         *ap++ = grabstackstr(outp);
  248.     }
  249.     if (ap == newargs + 1) {    /* if no args, maybe no exec is needed */
  250.         p = newargs[0];
  251.         for (;;) {
  252.             if (equal(p, "sh") || equal(p, "ash")) {
  253.                 return;
  254.             }
  255.             while (*p != '/') {
  256.                 if (*p == '\0')
  257.                     goto break2;
  258.                 p++;
  259.             }
  260.             p++;
  261.         }
  262. break2:;
  263.     }
  264.     i = (char *)ap - (char *)newargs;        /* size in bytes */
  265.     if (i == 0)
  266.         error("Bad #! line");
  267.     for (ap2 = argv ; *ap2++ != NULL ; );
  268.     new = ckmalloc(i + ((char *)ap2 - (char *)argv));
  269.     ap = newargs, ap2 = new;
  270.     while ((i -= sizeof (char **)) >= 0)
  271.         *ap2++ = *ap++;
  272.     ap = argv;
  273.     while (*ap2++ = *ap++);
  274.  
  275.     TRACE(("executing #! interpreter \"%s\"\n", argv[0]));
  276.     shellexec(new, envp, pathval(), 0);
  277. }
  278. #endif
  279.  
  280.  
  281.  
  282. /*
  283.  * Do a path search.  The variable path (passed by reference) should be
  284.  * set to the start of the path before the first call; padvance will update
  285.  * this value as it proceeds.  Successive calls to padvance will return
  286.  * the possible path expansions in sequence.  If an option (indicated by
  287.  * a percent sign) appears in the path entry then the global variable
  288.  * pathopt will be set to point to it; otherwise pathopt will be set to
  289.  * NULL.
  290.  */
  291.  
  292. char *pathopt;
  293.  
  294. char *
  295. padvance(path, name)
  296.     char **path;
  297.     char *name;
  298.     {
  299.     register char *p, *q;
  300.     char *start;
  301.     int len;
  302.  
  303.     if (*path == NULL)
  304.         return NULL;
  305.     start = *path;
  306.     for (p = start ; *p && *p != ':' && *p != '%' ; p++);
  307. #ifndef __MINT__
  308.     len = p - start + strlen(name) + 2;    /* "2" is for '/' and '\0' */
  309. #else
  310.     len = p - start + strlen(name) + 6;    /* "2" for /, suffix, and \0 */
  311. #endif
  312.     while (stackblocksize() < len)
  313.         growstackblock();
  314.     q = stackblock();
  315.     if (p != start) {
  316.         bcopy(start, q, p - start);
  317.         q += p - start;
  318.         *q++ = '/';
  319.     }
  320.     strcpy(q, name);
  321.     pathopt = NULL;
  322.     if (*p == '%') {
  323.         pathopt = ++p;
  324.         while (*p && *p != ':')  p++;
  325.     }
  326.     if (*p == ':')
  327.         *path = p + 1;
  328.     else
  329.         *path = NULL;
  330.     return stalloc(len);
  331. }
  332.  
  333.  
  334.  
  335. /*** Command hashing code ***/
  336.  
  337.  
  338. hashcmd(argc, argv)  char **argv; {
  339.     struct tblentry **pp;
  340.     struct tblentry *cmdp;
  341.     int c;
  342.     int verbose;
  343.     struct cmdentry entry;
  344.     char *name;
  345.  
  346.     if (argc <= 1) {
  347.         for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) {
  348.             for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
  349.                 printentry(cmdp);
  350.             }
  351.         }
  352.         return 0;
  353.     }
  354.     verbose = 0;
  355.     while ((c = nextopt("rv")) != '\0') {
  356.         if (c == 'r') {
  357.             clearcmdentry(0);
  358.         } else if (c == 'v') {
  359.             verbose++;
  360.         }
  361.     }
  362.     while ((name = *argptr) != NULL) {
  363.         if ((cmdp = cmdlookup(name, 0)) != NULL
  364.          && (cmdp->cmdtype == CMDNORMAL
  365.              || cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0))
  366.             delete_cmd_entry();
  367.         find_command(name, &entry, 1);
  368.         if (verbose) {
  369.             if (entry.cmdtype != CMDUNKNOWN) {    /* if no error msg */
  370.                 cmdp = cmdlookup(name, 0);
  371.                 printentry(cmdp);
  372.             }
  373.             flushall();
  374.         }
  375.         argptr++;
  376.     }
  377.     return 0;
  378. }
  379.  
  380.  
  381. STATIC void
  382. printentry(cmdp)
  383.     struct tblentry *cmdp;
  384.     {
  385.     int index;
  386.     char *path;
  387.     char *name;
  388.  
  389.     if (cmdp->cmdtype == CMDNORMAL) {
  390.         index = cmdp->param.index;
  391.         path = pathval();
  392.         do {
  393.             name = padvance(&path, cmdp->cmdname);
  394.             stunalloc(name);
  395.         } while (--index >= 0);
  396.         out1str(name);
  397.     } else if (cmdp->cmdtype == CMDBUILTIN) {
  398.         out1fmt("builtin %s", cmdp->cmdname);
  399.     } else if (cmdp->cmdtype == CMDFUNCTION) {
  400.         out1fmt("function %s", cmdp->cmdname);
  401. #ifdef DEBUG
  402.     } else {
  403.         error("internal error: cmdtype %d", cmdp->cmdtype);
  404. #endif
  405.     }
  406.     if (cmdp->rehash)
  407.         out1c('*');
  408.     out1c('\n');
  409. }
  410.  
  411.  
  412.  
  413. /*
  414.  * Resolve a command name.  If you change this routine, you may have to
  415.  * change the shellexec routine as well.
  416.  */
  417.  
  418. void
  419. find_command(name, entry, printerr)
  420.     char *name;
  421.     struct cmdentry *entry;
  422.     {
  423.     struct tblentry *cmdp;
  424.     int index;
  425.     int prev;
  426.     char *path;
  427.     char *fullname;
  428.     struct stat statb;
  429.     int e;
  430.     int i;
  431. #ifdef __MINT__
  432.     const char *const *suffix;
  433.     char *name_end;
  434. #endif
  435.  
  436.     /* If name contains a slash, don't use the hash table */
  437.     if (strchr(name, '/') != NULL) {
  438.         entry->cmdtype = CMDNORMAL;
  439.         entry->u.index = 0;
  440.         return;
  441.     }
  442.  
  443.     /* If name is in the table, and not invalidated by cd, we're done */
  444.     if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->rehash == 0)
  445.         goto success;
  446.  
  447.     /* If %builtin not in path, check for builtin next */
  448.     if (builtinloc < 0 && (i = find_builtin(name)) >= 0) {
  449.         INTOFF;
  450.         cmdp = cmdlookup(name, 1);
  451.         cmdp->cmdtype = CMDBUILTIN;
  452.         cmdp->param.index = i;
  453.         INTON;
  454.         goto success;
  455.     }
  456.  
  457.     /* We have to search path. */
  458.     prev = -1;        /* where to start */
  459.     if (cmdp) {        /* doing a rehash */
  460.         if (cmdp->cmdtype == CMDBUILTIN)
  461.             prev = builtinloc;
  462.         else
  463.             prev = cmdp->param.index;
  464.     }
  465.  
  466.     path = pathval();
  467.     e = ENOENT;
  468.     index = -1;
  469. loop:
  470.     while ((fullname = padvance(&path, name)) != NULL) {
  471.         stunalloc(fullname);
  472.         index++;
  473.         if (pathopt) {
  474.             if (prefix("builtin", pathopt)) {
  475.                 if ((i = find_builtin(name)) < 0)
  476.                     goto loop;
  477.                 INTOFF;
  478.                 cmdp = cmdlookup(name, 1);
  479.                 cmdp->cmdtype = CMDBUILTIN;
  480.                 cmdp->param.index = i;
  481.                 INTON;
  482.                 goto success;
  483.             } else if (prefix("func", pathopt)) {
  484.                 /* handled below */
  485.             } else {
  486.                 goto loop;    /* ignore unimplemented options */
  487.             }
  488.         }
  489.         /* if rehash, don't redo absolute path names */
  490.         if (fullname[0] == '/' && index <= prev) {
  491.             if (index < prev)
  492.                 goto loop;
  493.             TRACE(("searchexec \"%s\": no change\n", name));
  494.             goto success;
  495.         }
  496. #ifdef __MINT__
  497.         suffix = suffixes;
  498.         name_end = fullname + strlen (fullname);
  499.         for (suffix = suffixes; *suffix != 0; suffix++) {
  500.             (void) strcpy (name_end, *suffix);
  501.             TRACE(("find_command: trying to stat \"%s\"\n", fullname));
  502.             if (stat (fullname, &statb) >= 0)
  503.                 break;
  504.         }
  505.         if (*suffix == 0)
  506.             /* All suffixes tried to no avail */
  507.             goto loop;
  508. #else
  509.         while (stat(fullname, &statb) < 0) {
  510. #ifdef SYSV
  511.             if (errno == EINTR)
  512.                 continue;
  513. #endif
  514.             if (errno != ENOENT && errno != ENOTDIR)
  515.                 e = errno;
  516.             goto loop;
  517.         }
  518. #endif
  519.         e = EACCES;    /* if we fail, this will be the error */
  520.         if ((statb.st_mode & S_IFMT) != S_IFREG)
  521.             goto loop;
  522.         if (pathopt) {        /* this is a %func directory */
  523.             stalloc(strlen(fullname) + 1);
  524.             readcmdfile(fullname);
  525.             if ((cmdp = cmdlookup(name, 0)) == NULL || cmdp->cmdtype != CMDFUNCTION)
  526.                 error("%s not defined in %s", name, fullname);
  527.             stunalloc(fullname);
  528.             goto success;
  529.         }
  530.         if (statb.st_uid == geteuid()) {
  531.             if ((statb.st_mode & 0100) == 0)
  532.                 goto loop;
  533.         } else if (statb.st_gid == getegid()) {
  534.             if ((statb.st_mode & 010) == 0)
  535.                 goto loop;
  536.         } else {
  537.             if ((statb.st_mode & 01) == 0)
  538.                 goto loop;
  539.         }
  540.         TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
  541.         INTOFF;
  542.         cmdp = cmdlookup(name, 1);
  543.         cmdp->cmdtype = CMDNORMAL;
  544.         cmdp->param.index = index;
  545.         INTON;
  546.         goto success;
  547.     }
  548.  
  549.     /* We failed.  If there was an entry for this command, delete it */
  550.     if (cmdp)
  551.         delete_cmd_entry();
  552.     if (printerr)
  553.         outfmt(out2, "%s: %s\n", name, errmsg(e, E_EXEC));
  554.     entry->cmdtype = CMDUNKNOWN;
  555.     return;
  556.  
  557. success:
  558.     cmdp->rehash = 0;
  559.     entry->cmdtype = cmdp->cmdtype;
  560.     entry->u = cmdp->param;
  561. }
  562.  
  563.  
  564.  
  565. /*
  566.  * Search the table of builtin commands.
  567.  */
  568.  
  569. int
  570. find_builtin(name)
  571.     char *name;
  572.     {
  573.     const register struct builtincmd *bp;
  574.  
  575.     for (bp = builtincmd ; bp->name ; bp++) {
  576.         if (*bp->name == *name && equal(bp->name, name))
  577.             return bp->code;
  578.     }
  579.     return -1;
  580. }
  581.  
  582.  
  583.  
  584. /*
  585.  * Called when a cd is done.  Marks all commands so the next time they
  586.  * are executed they will be rehashed.
  587.  */
  588.  
  589. void
  590. hashcd() {
  591.     struct tblentry **pp;
  592.     struct tblentry *cmdp;
  593.  
  594.     for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) {
  595.         for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
  596.             if (cmdp->cmdtype == CMDNORMAL
  597.              || cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0)
  598.                 cmdp->rehash = 1;
  599.         }
  600.     }
  601. }
  602.  
  603.  
  604.  
  605. /*
  606.  * Called before PATH is changed.  The argument is the new value of PATH;
  607.  * pathval() still returns the old value at this point.  Called with
  608.  * interrupts off.
  609.  */
  610.  
  611. void
  612. changepath(newval)
  613.     char *newval;
  614.     {
  615.     char *old, *new;
  616.     int index;
  617.     int firstchange;
  618.     int bltin;
  619.  
  620.     old = pathval();
  621.     new = newval;
  622.     firstchange = 9999;    /* assume no change */
  623.     index = 0;
  624.     bltin = -1;
  625.     for (;;) {
  626.         if (*old != *new) {
  627.             firstchange = index;
  628.             if (*old == '\0' && *new == ':'
  629.              || *old == ':' && *new == '\0')
  630.                 firstchange++;
  631.             old = new;    /* ignore subsequent differences */
  632.         }
  633.         if (*new == '\0')
  634.             break;
  635.         if (*new == '%' && bltin < 0 && prefix("builtin", new + 1))
  636.             bltin = index;
  637.         if (*new == ':') {
  638.             index++;
  639.         }
  640.         new++, old++;
  641.     }
  642.     if (builtinloc < 0 && bltin >= 0)
  643.         builtinloc = bltin;        /* zap builtins */
  644.     if (builtinloc >= 0 && bltin < 0)
  645.         firstchange = 0;
  646.     clearcmdentry(firstchange);
  647.     builtinloc = bltin;
  648. }
  649.  
  650.  
  651. /*
  652.  * Clear out command entries.  The argument specifies the first entry in
  653.  * PATH which has changed.
  654.  */
  655.  
  656. STATIC void
  657. clearcmdentry(firstchange) {
  658.     struct tblentry **tblp;
  659.     struct tblentry **pp;
  660.     struct tblentry *cmdp;
  661.  
  662.     INTOFF;
  663.     for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) {
  664.         pp = tblp;
  665.         while ((cmdp = *pp) != NULL) {
  666.             if (cmdp->cmdtype == CMDNORMAL && cmdp->param.index >= firstchange
  667.              || cmdp->cmdtype == CMDBUILTIN && builtinloc >= firstchange) {
  668.                 *pp = cmdp->next;
  669.                 ckfree(cmdp);
  670.             } else {
  671.                 pp = &cmdp->next;
  672.             }
  673.         }
  674.     }
  675.     INTON;
  676. }
  677.  
  678.  
  679. /*
  680.  * Delete all functions.
  681.  */
  682.  
  683. #ifdef mkinit
  684. MKINIT void deletefuncs();
  685.  
  686. SHELLPROC {
  687.     deletefuncs();
  688. }
  689. #endif
  690.  
  691. void
  692. deletefuncs() {
  693.     struct tblentry **tblp;
  694.     struct tblentry **pp;
  695.     struct tblentry *cmdp;
  696.  
  697.     INTOFF;
  698.     for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) {
  699.         pp = tblp;
  700.         while ((cmdp = *pp) != NULL) {
  701.             if (cmdp->cmdtype == CMDFUNCTION) {
  702.                 *pp = cmdp->next;
  703.                 freefunc(cmdp->param.func);
  704.                 ckfree(cmdp);
  705.             } else {
  706.                 pp = &cmdp->next;
  707.             }
  708.         }
  709.     }
  710.     INTON;
  711. }
  712.  
  713.  
  714.  
  715. /*
  716.  * Locate a command in the command hash table.  If "add" is nonzero,
  717.  * add the command to the table if it is not already present.  The
  718.  * variable "lastcmdentry" is set to point to the address of the link
  719.  * pointing to the entry, so that delete_cmd_entry can delete the
  720.  * entry.
  721.  */
  722.  
  723. struct tblentry **lastcmdentry;
  724.  
  725.  
  726. STATIC struct tblentry *
  727. cmdlookup(name, add)
  728.     char *name;
  729.     {
  730.     int hashval;
  731.     register char *p;
  732.     struct tblentry *cmdp;
  733.     struct tblentry **pp;
  734.  
  735.     p = name;
  736.     hashval = *p << 4;
  737.     while (*p)
  738.         hashval += *p++;
  739.     hashval &= 0x7FFF;
  740.     pp = &cmdtable[hashval % CMDTABLESIZE];
  741.     for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
  742.         if (equal(cmdp->cmdname, name))
  743.             break;
  744.         pp = &cmdp->next;
  745.     }
  746.     if (add && cmdp == NULL) {
  747.         INTOFF;
  748.         cmdp = *pp = ckmalloc(sizeof (struct tblentry) - ARB
  749.                     + strlen(name) + 1);
  750.         cmdp->next = NULL;
  751.         cmdp->cmdtype = CMDUNKNOWN;
  752.         cmdp->rehash = 0;
  753.         strcpy(cmdp->cmdname, name);
  754.         INTON;
  755.     }
  756.     lastcmdentry = pp;
  757.     return cmdp;
  758. }
  759.  
  760.  
  761. /*
  762.  * Delete the command entry returned on the last lookup.
  763.  */
  764.  
  765. STATIC void
  766. delete_cmd_entry() {
  767.     struct tblentry *cmdp;
  768.  
  769.     INTOFF;
  770.     cmdp = *lastcmdentry;
  771.     *lastcmdentry = cmdp->next;
  772.     ckfree(cmdp);
  773.     INTON;
  774. }
  775.  
  776.  
  777.  
  778. #ifdef notdef
  779. void
  780. getcmdentry(name, entry)
  781.     char *name;
  782.     struct cmdentry *entry; 
  783.     {
  784.     struct tblentry *cmdp = cmdlookup(name, 0);
  785.  
  786.     if (cmdp) {
  787.         entry->u = cmdp->param;
  788.         entry->cmdtype = cmdp->cmdtype;
  789.     } else {
  790.         entry->cmdtype = CMDUNKNOWN;
  791.         entry->u.index = 0;
  792.     }
  793. }
  794. #endif
  795.  
  796.  
  797. /*
  798.  * Add a new command entry, replacing any existing command entry for
  799.  * the same name.
  800.  */
  801.  
  802. void
  803. addcmdentry(name, entry)
  804.     char *name;
  805.     struct cmdentry *entry;
  806.     {
  807.     struct tblentry *cmdp;
  808.  
  809.     INTOFF;
  810.     cmdp = cmdlookup(name, 1);
  811.     if (cmdp->cmdtype == CMDFUNCTION) {
  812.         freefunc(cmdp->param.func);
  813.     }
  814.     cmdp->cmdtype = entry->cmdtype;
  815.     cmdp->param = entry->u;
  816.     INTON;
  817. }
  818.  
  819.  
  820. /*
  821.  * Define a shell function.
  822.  */
  823.  
  824. void
  825. defun(name, func)
  826.     char *name;
  827.     union node *func;
  828.     {
  829.     struct cmdentry entry;
  830.  
  831.     INTOFF;
  832.     entry.cmdtype = CMDFUNCTION;
  833.     entry.u.func = copyfunc(func);
  834.     addcmdentry(name, &entry);
  835.     INTON;
  836. }
  837.  
  838.  
  839. /*
  840.  * Delete a function if it exists.
  841.  */
  842.  
  843. void
  844. unsetfunc(name)
  845.     char *name;
  846.     {
  847.     struct tblentry *cmdp;
  848.  
  849.     if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->cmdtype == CMDFUNCTION) {
  850.         freefunc(cmdp->param.func);
  851.         delete_cmd_entry();
  852.     }
  853. }
  854.